#import "BaseDeviceInfoPlugin.h"
#include <sys/sysctl.h>

#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <PushKit/PushKit.h>
#import <mach/mach.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <EventKit/EventKit.h>


@implementation BaseDeviceInfoPlugin


+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"base_device_info"
                                     binaryMessenger:[registrar messenger]];
    BaseDeviceInfoPlugin* instance = [[BaseDeviceInfoPlugin alloc] init];
    
    [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    if ([@"getPlatformInfo" isEqualToString:call.method]) {
        
        NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
        
        NSTimeInterval a=[dat timeIntervalSince1970] * 1000;
        
        _openTime = [NSString stringWithFormat:@"%0.f", a];//转为字符型
        
        result(@[
            [self getDeviceBaseInfo],
            [self getGeneralInfo],
            [self getLocaleInfo],
            [self getScreenInfo],
            [self getStorageInfo],
            [self getBatteryStatus],
            [self getBaseInfo],
            [self getProcessInfo],
        ]);
    } else if([@"getPlatformCalendar" isEqualToString:call.method]) {
        
        result(@[]);
        // result([self fetchCalendarEvents]);
        
    }else {
        result(FlutterMethodNotImplemented);
    }
}
/**
 *  查日历事件(可查询一段时间内的事件)
 *
 *  @param startDate  开始时间
 *  @param endDate    结束时间
 *  @param modifytitle    标题，为空则都要查询
 *  @param calendarIdentifier  事件源(传nil，则为默认)
 */
//- (NSArray *)checkToStartDate:(NSDate *)startDate addEndDate:(NSDate *)endDate addModifytitle:(NSString *)modifytitle addCalendarIdentifier:(NSString *)calendarIdentifier{
//    
//    EKEventStore *eventStore = [[EKEventStore alloc] init];
//
//    // 查询到所有的日历
//    NSArray *tempA = [eventStore calendarsForEntityType:EKEntityTypeEvent];
//    NSMutableArray *only3D = [NSMutableArray array];
//    
//    for (int i = 0 ; i < tempA.count; i ++) {
//        
//        EKCalendar *temCalendar = tempA[i];
//        EKCalendarType type = temCalendar.type;
//        // 工作、家庭和本地日历
//        if (type == EKCalendarTypeLocal || type == EKCalendarTypeCalDAV)  {
//            if (calendarIdentifier && ![calendarIdentifier isEqualToString:@""]) {
//                NSString *cIdentifier = [[NSUserDefaults standardUserDefaults] objectForKey:calendarIdentifier];
//                if ([temCalendar.calendarIdentifier isEqualToString:cIdentifier]){
//                    [only3D addObject:temCalendar];
//                }
//            }else{
//                [only3D addObject:temCalendar];
//            }
//        }
//    }
//    
//    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:only3D];
//    
//    // 获取到范围内的所有事件
//    NSArray *request = [eventStore eventsMatchingPredicate:predicate];
//    // 按开始事件进行排序
//    request = [request sortedArrayUsingSelector:@selector(compareStartDateWithEvent:)];
//    
//    if (!modifytitle || [modifytitle isEqualToString:@""]) {
//        return request;
//    }else{
//        NSMutableArray *onlyRequest = [NSMutableArray array];
//        for (int i = 0; i < request.count; i++) {
//            EKEvent *event = request[i];
//            if (event.title && [event.title isEqualToString:modifytitle]) {
//                [onlyRequest addObject:event];
//            }
//        }
//        return onlyRequest;
//    }
//}
 

- (NSArray *)fetchCalendarEvents {
    // 创建事件存储
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    
    // 设置开始和结束时间（例如：今天到一周后）
    NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:-365 * 24 * 60 * 60];
    NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:365 * 24 * 60 * 60];
    
    
    // 创建谓词查询
    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:[eventStore calendarsForEntityType:EKEntityTypeEvent]];
    
    // 获取事件
    NSArray *events = [eventStore eventsMatchingPredicate:predicate];
    
    NSMutableArray *res = @[].mutableCopy;
    for (EKEvent *event in events) {
        [res addObject: @[
            event.eventIdentifier,
            event.title,
            event.notes,
            event.location,
            event.startDate,
            event.endDate,
            event.hasAlarms ? @1 : @0,
            NSNull.null,
        ]];
    }
    
    return res;
}


/*
 DeviceBaseInfo数组
 */

- (NSArray *)getDeviceBaseInfo {
    NSArray *data = @[
        @"Apple",
        UIDevice.currentDevice.name,
        UIDevice.currentDevice.name,
        @0,
        UIDevice.currentDevice.systemVersion,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        @(countOfCores()),
        NSNull.null,
        NSNull.null,
        NSNull.null,
    ];
    return  data;
    
}


unsigned int countOfCores(void)
{
    unsigned int ncpu;
    size_t len = sizeof(ncpu);
    sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0);
    //当前电源管理模式下可用的物理处理器数量。
    //sysctlbyname("hw.physicalcpu", &ncpu, &len, NULL, 0);
    //此引导中可用的最大物理处理器数。
    //sysctlbyname("hw.physicalcpu_max", &ncpu, &len, NULL, 0);
    //当前电源管理模式下可用的逻辑处理器数量。
    //sysctlbyname("hw.logicalcpu", &ncpu, &len, NULL, 0);
    //可用于此引导的最大逻辑处理器数。
    //sysctlbyname("hw.logicalcpu_max", &ncpu, &len, NULL, 0);
    return ncpu;
    
}



/*
 GeneralInfo数组
 */

- (NSArray *)getGeneralInfo {
    NSString *deviceType = [UIDevice currentDevice].model;
    Boolean isCall = [deviceType  isEqualToString:@"iPod touch"]||[deviceType  isEqualToString:@"iPad"]||[deviceType  isEqualToString:@"iPhone Simulator"];
    
    
    NSArray *data = @[
        @([self isJailBreak]),
        isCall ? @1 : @0 ,
        @1,
        [self isConnectedToProxy] ? @1 : @0 ,
        [self isVPNActive] ? @1 : @0 ,
        [self isDebug] ? @1 : @0 ,
        _openTime,
        @0,
        @([self systemUptime]),
        @([self systemUptime]),
        (TARGET_IPHONE_SIMULATOR == 1 && TARGET_OS_IPHONE == 1) ? @1 : @0 ,
    ];
    return  data;
}

- (BOOL)isDebug {
#ifdef DEBUG
    return  YES;
#else
    return  NO;
#endif
}

- (BOOL)isVPNActive {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com");
    SCNetworkReachabilityFlags flags;
    BOOL isReachable = SCNetworkReachabilityGetFlags(reachability, &flags);
    CFRelease(reachability);
    
    if (!isReachable) {
        return NO;
    }
    
    // 检查是否连接了 VPN
    BOOL isUsingVPN = (flags & kSCNetworkReachabilityFlagsIsWWAN) == 0 && (flags & kSCNetworkReachabilityFlagsReachable);
    return isUsingVPN;
}


// 判断是否使用代理了
- (BOOL)isConnectedToProxy {
    
    NSDictionary *proxySettings = (__bridge NSDictionary *)CFNetworkCopySystemProxySettings();
    NSArray * proxies = (__bridge NSArray *)CFNetworkCopyProxiesForURL((__bridge CFURLRef)[NSURL URLWithString:@"https://www.apple.com"], (__bridge CFDictionaryRef)proxySettings);
    
    NSDictionary *settings = [proxies objectAtIndex:0];
    
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"])
    {
        //没有设置代理
        return NO;
    }
    else
    {
        //设置代理了
        return YES;
    }
}


-(NSInteger) systemUptime
{
    struct timeval t;
    size_t len=sizeof(struct timeval);
    if(sysctlbyname("kern.boottime",&t,&len,0,0)!=0)
        return 0.0;
    return  (NSInteger)(t.tv_sec+t.tv_usec/USEC_PER_SEC) * 1000 ;
}

//检测当前程序运行的环境变量，防止通过DYLD_INSERT_LIBRARIES注入链接异常动态库，来更改相关工具名称
- (NSInteger)isJailBreak
{
    char *env = getenv("DYLD_INSERT_LIBRARIES");
    if (env) {
        return 1;
    }
    return 0;
}

/*
 LocaleInfo 数组
 */
- (NSArray *)getLocaleInfo {
    // iOS 获取设备当前语言的代码
    NSString *preferredLanguage = [[[NSBundle mainBundle] preferredLocalizations] firstObject];
    // iOS 获取设备当前地区的代码
    NSString *localeIdentifier = [[NSLocale currentLocale] objectForKey:NSLocaleIdentifier];
    
    NSArray *data = @[
        preferredLanguage,
        localeIdentifier,
        preferredLanguage,
        localeIdentifier,
        NSTimeZone.systemTimeZone.name,
        NSNull.null,
        @"- -"
    ];
    return data;
}


/*
 ScreenInfo 数组 （DisplayMetrics）
 */
- (NSArray *)getScreenInfo {
    NSInteger width = (NSInteger)[UIScreen mainScreen].bounds.size.width;
    NSInteger height = (NSInteger)[UIScreen mainScreen].bounds.size.height;
    CGFloat scale = [UIScreen mainScreen].scale;
    
    NSArray *data =  @[
        @(width),
        @(height),
        @(scale*width),
        @(scale*height),
        NSNull.null,
        NSNull.null,
        [NSString stringWithFormat:@"%.fx%.f", scale*width, scale*height],
    ];
    return  data;
}

/*
 StorageInfo 数组
 */
- (NSArray *)getStorageInfo {
    NSArray *data =  @[
        @(MemoryUtil.getTotalMemory),
        @(MemoryUtil.getFreeMemory),
        @(MemoryUtil.usedDiskSpaceInBytes),
        @(MemoryUtil.totalDiskSpaceInBytes),
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        NSNull.null,
        @(MemoryUtil.getPurgableMemory),
        @(MemoryUtil.getInActiveMemory),
        @(MemoryUtil.getActiveMemory),
        @(MemoryUtil.getWiredMemory),
        @(MemoryUtil.getUsedMemory),
        NSNull.null,
        NSNull.null,
        
    ];
    return data;
}

/*
 BatteryStatus 数组
 */
- (NSArray *)getBatteryStatus {
    
    UIDevice *device = [UIDevice currentDevice];
    [device setBatteryMonitoringEnabled:YES];
    
    NSArray *data =  @[
        device.batteryState == UIDeviceBatteryStateCharging ? @1 : @0,
        @(device.batteryLevel * 100),
        [(__bridge NSString*)CFSTR("com.apple.mobile.lockdown.host_attached") isEqualToString:@"com.apple.mobile.lockdown.host_attached"] ? @1 : @0,
        device.batteryState == UIDeviceBatteryStateCharging ? @1 : @0,
        NSNull.null,
        NSNull.null,
        device.batteryState == UIDeviceBatteryStateCharging ? @1 : @0,
    ];
    
    
    return  data;
}




/*
 IosInfo IOS基本信息
 */
- (NSArray *)getBaseInfo {
    CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init];
 __block   NSString *mobileCountryCode;
    __block   NSString *mobileNetworkCode;
    if (@available(iOS 12.0, *)) {
        NSDictionary *carrierDic = networkInfo.serviceSubscriberCellularProviders;

        [carrierDic.allValues enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            CTCarrier *carrier = obj;
            //NSLog(@"carrier = %@", carrier);
            mobileCountryCode = carrier.mobileCountryCode;
            mobileNetworkCode = carrier.mobileNetworkCode;
        }];
    }
    
    
    PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
    
    
    NSArray *data =  @[
        [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
        [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
        [[NSLocale preferredLanguages] firstObject] ?:  @"flutter",
        [NSString stringWithFormat:@"%@", networkInfo.serviceCurrentRadioAccessTechnology],
        pushRegistry.desiredPushTypes.count != 0 ? @1 : @0,
        mobileCountryCode ?: @"65535",
        mobileNetworkCode ?: @"65535",
        
    ];
    
    return data;
}

/*
 ProcessInfo IOS进程信息
 */
- (NSArray *)getProcessInfo {
    NSProcessInfo *instance =   [NSProcessInfo processInfo];
    NSArray *data = @[
        instance.processName,
        @(instance.activeProcessorCount),
        @(instance.processIdentifier),
        @(instance.processorCount),
        @(instance.physicalMemory),
        instance.hostName,
        [NSString stringWithFormat:@"%@", instance.environment],
        [NSString stringWithFormat:@"%@", instance.arguments],
        instance.globallyUniqueString,
        instance.operatingSystemVersionString,
        @(instance.systemUptime),
        @(instance.thermalState),
        @(instance.lowPowerModeEnabled),
    ];
    
    return  data;
}


@end



@implementation MemoryUtil


// App占用内存
+ (int64_t)memoryUsage {
    int64_t memoryUsageInByte = 0;
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if(kernelReturn == KERN_SUCCESS) {
        memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
        //        NSLog(@"Memory in use (in bytes): %lld", memoryUsageInByte);
    } else {
        //        NSLog(@"Error with task_info(): %s", mach_error_string(kernelReturn));
    }
    return memoryUsageInByte;
}

// 总内存
+ (int64_t)getTotalMemory {
    int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
    if (totalMemory < -1) totalMemory = -1;
    return totalMemory;
}

// 活跃的内存,正在使用或者很短时间内被使用过:已使用，但可被分页的（在iOS中，只有在磁盘上静态存在的才能被分页，例如文件的内存映射，而动态分配的内存是不能被分页的）
+ (int64_t)getActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.active_count * page_size;
}

// 最近使用过,但是目前处于不活跃状态的内存:程序退出后却没释放的内存，以便加快再次启动，而当内存不足时，就会被回收，因此也可看作空闲内存
+ (int64_t)getInActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.inactive_count * page_size;
}

// 用来存放内核和数据结构的内存,framework、用户级别的应用无法分配
+ (int64_t)getWiredMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.wire_count * page_size;
}

// 已使用的内存空间
+ (int64_t)getUsedMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
}

// 空闲的内存空间,:未使用的 RAM 容量，随时可以被应用分配使用。（感觉本方法和腾讯bugly崩溃统计中的可用内存大小相似）
+ (int64_t)getFreeMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.free_count * page_size;
}

// 可释放的内存空间：内存吃紧自动释放，针对大对象存放所需的大块内存空间
+ (int64_t)getPurgableMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.purgeable_count * page_size;
}

// 获取当前可用内存
+ (long long)getAvailableMemorySize {
    vm_statistics_data_t vmStats;
    mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
    kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
    if (kernReturn != KERN_SUCCESS)
    {
        return NSNotFound;
    }
    return ((vm_page_size * vmStats.free_count + vm_page_size * vmStats.inactive_count));
}

+ (double)usageComparisonFree {
    int64_t physical_memory = [[NSProcessInfo processInfo] physicalMemory];
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
    
    vm_size_t page_size;
    vm_statistics64_data_t vminfo;
    host_page_size(host_port, &page_size);
    host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vminfo,&count);
    
    uint64_t total_used_count = (physical_memory /page_size) - (vminfo.free_count - vminfo.speculative_count) - vminfo.external_page_count - vminfo.purgeable_count;
    uint64_t free_size = ((physical_memory / page_size) -total_used_count) * page_size;
    return free_size / 1024.0 / 1024.0;
}




+ (long)totalDiskSpaceInBytes
{
    NSError * error = nil;
    NSDictionary<NSFileAttributeKey, id> * systemAttributes =  [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) {
        return 0;
    }
    long long space = [systemAttributes[NSFileSystemSize] longLongValue];
    return space;
}

+ (long)freeDiskSpaceInBytes
{
    if (@available(iOS 11.0, *)) {
        [NSURL alloc];
        NSURL * url = [[NSURL alloc]initFileURLWithPath:[NSString stringWithFormat:@"%@",NSHomeDirectory()]];
        NSError * error = nil;
        NSDictionary<NSURLResourceKey, id> * dict = [url resourceValuesForKeys:@[NSURLVolumeAvailableCapacityForImportantUsageKey] error:&error];
        if (error) {
            return 0;
        }
        long long space = [dict[NSURLVolumeAvailableCapacityForImportantUsageKey] longLongValue];
        return space;
    } else {
        NSError * error = nil;
        NSDictionary<NSFileAttributeKey, id> * systemAttributes =  [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
        if (error) {
            return 0;
        }
        long long space = [systemAttributes[NSFileSystemFreeSize] longLongValue];
        return space;
    }
}

+ (long)usedDiskSpaceInBytes
{
    return self.totalDiskSpaceInBytes - self.freeDiskSpaceInBytes;
}
@end
